home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / system-service / system-service-d
Encoding:
Text File  |  2009-03-10  |  19.1 KB  |  502 lines

  1. #!/usr/bin/python
  2.  
  3. # (c) 2008 Canonical Ltd.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along
  16. # with this program; if not, write to the Free Software Foundation, Inc.,
  17. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18.  
  19. import sys
  20. import gobject
  21. import dbus
  22. import dbus.service
  23. import dbus.mainloop.glib 
  24. import os
  25. import subprocess
  26. import apt_pkg
  27. import struct
  28. import fcntl
  29.  
  30. from UbuntuSystemService.utils import *
  31.  
  32. class UnknownProxyTypeError(dbus.DBusException):
  33.     " a unknown proxy type was passed "
  34.     pass
  35. class InvalidKeyboardTypeError(dbus.DBusException):
  36.     " a invalid keyboard was set "
  37.     pass
  38. class PermissionDeniedError(dbus.DBusException):
  39.     " permission denied by policy "
  40.     pass
  41.  
  42.  
  43. class ServiceBackend(dbus.service.Object): 
  44.     """ 
  45.     the main backend class that supports various system settings like
  46.     proxy and keyboard
  47.     """
  48.  
  49.     # some class properties
  50.     DBUS_INTERFACE_NAME = "com.ubuntu.SystemService"
  51.     SUPPORTED_PROXIES = ("http","ftp", "https", "socks")
  52.  
  53.     # default files
  54.     CONSOLE_SETUP_DEFAULT = "/etc/default/console-setup"
  55.     DPKG_LOCK = "/var/lib/dpkg/lock"
  56.  
  57.     def __init__(self):
  58.         bus_name = dbus.service.BusName(self.DBUS_INTERFACE_NAME,
  59.                                         bus=dbus.SystemBus())
  60.         dbus.service.Object.__init__(self, bus_name, '/')
  61.         apt_pkg.InitConfig()
  62.  
  63.     def _authWithPolicyKit(self, sender, connection, priv):
  64.         #print "_authWithPolicyKit()"
  65.         system_bus = dbus.SystemBus()
  66.         obj = system_bus.get_object("org.freedesktop.PolicyKit", 
  67.                                     "/", 
  68.                                     "org.freedesktop.PolicyKit")
  69.         policykit = dbus.Interface(obj, "org.freedesktop.PolicyKit")
  70.         info = dbus.Interface(connection.get_object('org.freedesktop.DBus',
  71.                                               '/org/freedesktop/DBus/Bus', 
  72.                                               False), 
  73.                               'org.freedesktop.DBus')
  74.         pid = info.GetConnectionUnixProcessID(sender) 
  75.         #print "pid is:",pid
  76.         #print "priv: ", priv
  77.         # FIXME: not sure what revoke_if_one_shot (last arg) does
  78.         ok = policykit.IsProcessAuthorized(priv, dbus.UInt32(pid), True)
  79.         #print "ok: ", ok
  80.         if ok == 'yes':
  81.             return True
  82.         return False
  83.  
  84.     # proxy stuff ---------------------------------------------------
  85.     def _etc_environment_proxy(self, proxy_type):
  86.         " internal that returns the /etc/environment proxy "
  87.         if not os.path.exists("/etc/environment"):
  88.             return ""
  89.         for line in open("/etc/environment"):
  90.             if line.startswith("%s_proxy=" % proxy_type):
  91.                 (key, value) = line.strip().split("=")
  92.                 value = value.strip('"')
  93.                 return value
  94.         return ""
  95.  
  96.     def _http_proxy(self):
  97.         " internal helper that returns the current http proxy "
  98.         apt_proxy = self._apt_proxy("http")
  99.         env_proxy = self._etc_environment_proxy("http")
  100.         # FIXME: what to do if both proxies are differnet?
  101.         return env_proxy
  102.  
  103.     def _apt_proxy(self, proxy_type):
  104.         " internal helper that returns the configured apt proxy"
  105.         apt_pkg.InitConfig()
  106.         proxy = apt_pkg.Config.Find("Acquire::%s::proxy" % proxy_type)
  107.         return proxy
  108.  
  109.     def _ftp_proxy(self):
  110.         apt_proxy = self._apt_proxy("ftp")
  111.         env_proxy = self._etc_environment_proxy("ftp")
  112.         # FIXME: what to do if both proxies are differnet?
  113.         return env_proxy
  114.  
  115.     def _socks_proxy(self):
  116.         env_proxy = self._etc_environment_proxy("socks")
  117.         return env_proxy
  118.  
  119.     def _ftp_apt_proxy(self):
  120.         " internal helper that returns the configured apt proxy"
  121.         apt_pkg.InitConfig()
  122.         http_proxy = apt_pkg.Config.Find("Acquire::ftp::proxy")
  123.         return http_proxy
  124.  
  125.     def _https_proxy(self):
  126.         " internal helper that returns the current https proxy "
  127.         env_proxy = self._etc_environment_proxy("https")
  128.         return env_proxy
  129.  
  130.     def _verify_proxy(self, proxy_type, proxy):
  131.         " internal helper, verify that the proxy string is valid "
  132.         return verify_proxy(proxy_type, proxy)
  133.  
  134.     def _verify_no_proxy(self, proxy):
  135.         " internal helper, verify that the no_proxy string is valid "
  136.         return verify_no_proxy(proxy)
  137.  
  138.     @dbus.service.method(DBUS_INTERFACE_NAME,
  139.                          in_signature='s', 
  140.                          out_signature='s',
  141.                          sender_keyword='sender',
  142.                          connection_keyword='conn')
  143.     def get_proxy(self, proxy_type, sender=None, conn=None):
  144.         """ 
  145.         Get the current system-wide proxy  for type "proxy_type"
  146.  
  147.         This function will look in the apt configuration to 
  148.         find the current http proxy.
  149.         """
  150.         if proxy_type == "http":
  151.             return self._http_proxy()
  152.         if proxy_type == "https":
  153.             return self._https_proxy()
  154.         elif proxy_type == "ftp": 
  155.             return self._ftp_proxy()
  156.         elif proxy_type == "socks": 
  157.             return self._socks_proxy()
  158.         raise UnknownProxyTypeError, "proxy_type '%s' is unknown in get_proxy" % proxy_type
  159.  
  160.  
  161.     def _write_apt_proxy(self, proxy_type, new_proxy):
  162.         " helper that writes the new apt proxy "
  163.         confdir = apt_pkg.Config.FindDir("Dir::Etc") 
  164.         if not self._verify_proxy(proxy_type, new_proxy):
  165.             return False
  166.         # check for the easy case (no proxy setting in the config)
  167.         old_proxy = self._apt_proxy(proxy_type)
  168.         if old_proxy == "":
  169.             f=open(os.path.join(confdir, "apt.conf"),"a")
  170.             f.write("Acquire::%s::proxy \"%s\";\n" % (proxy_type, new_proxy))
  171.             f.close()
  172.             return True
  173.         # now the difficult case (search the apt configuration files)
  174.         # build the list of apt configuration files first
  175.         apt_conffiles = [os.path.join(confdir,"apt.conf.d",n) for n in 
  176.                          os.listdir(os.path.join(confdir,"apt.conf.d"))]
  177.         apt_conffiles.insert(0, os.path.join(confdir,"apt.conf"))
  178.         # then scan them for the content
  179.         for f in apt_conffiles:
  180.             new_content = []
  181.             found = False
  182.             for line in open(f):
  183.                 if line.lower().startswith("acquire::%s::proxy" % proxy_type):
  184.                     found = True
  185.                     line = "Acquire::%s::proxy \"%s\";\n" % (proxy_type, new_proxy)
  186.                 # FIXME: scan for more complicated forms of the proxy
  187.                 # settings and/or scan for the proxy string and just
  188.                 # replace this
  189.                 new_content.append(line)
  190.             # if we found/replaced the proxy, write it out now
  191.             if found:
  192.                 open(f,"w").write("".join(new_content))
  193.                 return True
  194.         return False
  195.  
  196.     def _write_etc_environment_proxy(self, proxy_type, new_proxy):
  197.         if not self._verify_proxy(proxy_type, new_proxy):
  198.             return False
  199.         found=False
  200.         new_content=[]
  201.         new_proxy_line = '%s_proxy="%s"\n' % (proxy_type, new_proxy)
  202.         for line in open("/etc/environment"):
  203.             if line.startswith("%s_proxy=" % proxy_type):
  204.                 line=new_proxy_line
  205.                 found = True
  206.             new_content.append(line)
  207.         if found:
  208.             open("/etc/environment","w").write("".join(new_content))
  209.         else:
  210.             open("/etc/environment","a").write(new_proxy_line)
  211.         return True
  212.  
  213.     def _clear_etc_environment_proxy(self, proxy_type):
  214.         found=False
  215.         new_content=[]
  216.         for line in open("/etc/environment"):
  217.             if line.startswith("%s_proxy=" % proxy_type):
  218.                 found = True
  219.             else:
  220.                 new_content.append(line)
  221.         if found:
  222.             open("/etc/environment","w").write("".join(new_content))
  223.         return True
  224.     
  225.     def _clear_apt_proxy(self, proxy_type):
  226.         " helper that clears the apt proxy "
  227.         confdir = apt_pkg.Config.FindDir("Dir::Etc") 
  228.         apt_conffiles = [os.path.join(confdir,"apt.conf.d",n) for n in 
  229.                          os.listdir(os.path.join(confdir,"apt.conf.d"))]
  230.         apt_conffiles.insert(0, os.path.join(confdir,"apt.conf"))
  231.         for f in apt_conffiles:
  232.             new_content = []
  233.             found = False
  234.             for line in open(f):
  235.                 if line.lower().startswith("acquire::%s::proxy" % proxy_type):
  236.                     found = True
  237.                 else:
  238.                     new_content.append(line)
  239.             # if we found/replaced the proxy, write it out now
  240.             if found:
  241.                 open(f,"w").write("".join(new_content))
  242.         return True
  243.     
  244.     @dbus.service.method(DBUS_INTERFACE_NAME,
  245.                          in_signature='ss', 
  246.                          out_signature='b',
  247.                          sender_keyword='sender',
  248.                          connection_keyword='conn')
  249.     def set_proxy(self, proxy_type, new_proxy, sender=None, conn=None):
  250.         """
  251.         Set a new system-wide proxy that looks like e.g.:
  252.         http://proxy.host.net:port/
  253.  
  254.         This function will set a new apt configuration and
  255.         modify /etc/environment
  256.         
  257.         """
  258.         if not self._authWithPolicyKit(sender, conn, 
  259.                                        "com.ubuntu.systemservice.setproxy"):
  260.             if not self._authWithPolicyKit(sender, conn,
  261.                                            "org.gnome.gconf.defaults.set-system"):
  262.                 raise PermissionDeniedError, "Permission denied by policy"
  263.         
  264.         # check if something supported is set
  265.         if not proxy_type in self.SUPPORTED_PROXIES:
  266.             raise UnknownProxyTypeError, "proxy_type '%s' is unknown in set_proxy" % proxy_type
  267.         
  268.         # set (or reset)
  269.         if new_proxy == "" or new_proxy is None:
  270.             res = self._clear_apt_proxy(proxy_type)
  271.             res &= self._clear_etc_environment_proxy(proxy_type)
  272.         else:
  273.             res = self._write_apt_proxy(proxy_type, new_proxy)
  274.             res &= self._write_etc_environment_proxy(proxy_type, new_proxy)
  275.         return res
  276.  
  277.  
  278.     def _clear_etc_environment_no_proxy(self):
  279.         found=False
  280.         new_content=[]
  281.         for line in open("/etc/environment"):
  282.             if line.startswith("no_proxy="):
  283.                 found = True
  284.             else:
  285.                 new_content.append(line)
  286.         if found:
  287.             open("/etc/environment","w").write("".join(new_content))
  288.         return True
  289.  
  290.     def _write_etc_environment_no_proxy(self, new_proxy):
  291.         if not self._verify_no_proxy(new_proxy):
  292.             return False
  293.         found=False
  294.         new_content=[]
  295.         new_proxy_line = 'no_proxy="%s"\n' % new_proxy
  296.         for line in open("/etc/environment"):
  297.             if line.startswith("no_proxy="):
  298.                 line=new_proxy_line
  299.                 found = True
  300.             new_content.append(line)
  301.         if found:
  302.             open("/etc/environment","w").write("".join(new_content))
  303.         else:
  304.             open("/etc/environment","a").write(new_proxy_line)
  305.         return True
  306.  
  307.     @dbus.service.method(DBUS_INTERFACE_NAME,
  308.                          in_signature='s', 
  309.                          out_signature='b',
  310.                          sender_keyword='sender',
  311.                          connection_keyword='conn')
  312.     def set_no_proxy(self, new_no_proxy, sender=None, conn=None):
  313.         """
  314.         Set a new system-wide no_proxy list that looks like e.g.:
  315.         localhost,foo.com
  316.  
  317.         This function will modify /etc/environment
  318.         
  319.         """
  320.         if not self._authWithPolicyKit(sender, conn, 
  321.                                        "com.ubuntu.systemservice.setnoproxy"):
  322.             if not self._authWithPolicyKit(sender, conn,
  323.                                            "org.gnome.gconf.defaults.set-system"):
  324.                 raise PermissionDeniedError, "Permission denied by policy"
  325.         
  326.         # set (or reset)
  327.         if new_no_proxy == "" or new_no_proxy is None:
  328.             res = self._clear_no_proxy()
  329.         else:
  330.             res = self._write_etc_environment_no_proxy(new_no_proxy)
  331.         return res
  332.  
  333.     # keyboard stuff ---------------------------------------------------
  334.     def _get_keyboard_from_etc(self):
  335.         """ 
  336.         helper that reads /etc/default/console-setup and gets the 
  337.         keyboard settings there
  338.         """
  339.         model = ""
  340.         layout = ""
  341.         variant = ""
  342.         options = ""
  343.         for line in open(self.CONSOLE_SETUP_DEFAULT):
  344.             if line.startswith("XKBMODEL="):
  345.                 model = line.split("=")[1].strip('"\n')
  346.             elif line.startswith("XKBLAYOUT="):
  347.                 layout = line.split("=")[1].strip('"\n')
  348.             elif line.startswith("XKBVARIANT="):
  349.                 variant = line.split("=")[1].strip('"\n')
  350.             elif line.startswith("XKBOPTIONS="):
  351.                 options = line.split("=")[1].strip('"\n')
  352.         return (model, layout, variant, options)
  353.  
  354.     @dbus.service.method(DBUS_INTERFACE_NAME,
  355.                          in_signature='', 
  356.                          out_signature='ssss',
  357.                          sender_keyword='sender',
  358.                          connection_keyword='conn')
  359.     def get_keyboard(self, sender=None, conn=None):
  360.         """
  361.         Set the system default keyboard configuration. 
  362.  
  363.         It expects four input arguments (strings):
  364.         model -- the model (evdev, pc105, ...)
  365.         layout -- the layout (de, us, ...)
  366.         variant -- the variant (nodeadkeys, ..)
  367.         options -- keyboard options (nocaps, ...)
  368.  
  369.         It returns True on sucess
  370.         """
  371.         (model, layout, variant, options) = self._get_keyboard_from_etc()
  372.         return (model, layout, variant, options)
  373.  
  374.     def _set_keyboard_to_etc(self, model, layout, variant, options):
  375.         """ 
  376.         helper that writes /etc/default/console-setup 
  377.         """
  378.         #print "set_keyboard_to_etc"
  379.         # FIXME: what to do if not os.path.exists(self.CONSOLE_SETUP_DEFAULT)
  380.         content = []
  381.         for line in open(self.CONSOLE_SETUP_DEFAULT):
  382.             if line.startswith("XKBMODEL="):
  383.                 line = 'XKBMODEL="%s"\n' % model
  384.             elif line.startswith("XKBLAYOUT="):
  385.                 line = 'XKBLAYOUT="%s"\n' % layout
  386.             elif line.startswith("XKBVARIANT="):
  387.                 line = 'XKBVARIANT="%s"\n' % variant
  388.             elif line.startswith("XKBOPTIONS="):
  389.                 line = 'XKBOPTIONS="%s"\n' % options
  390.             content.append(line)
  391.         # if something changed, write 
  392.         if content != open(self.CONSOLE_SETUP_DEFAULT).readlines():
  393.             #print "content changed, writing"
  394.             open(self.CONSOLE_SETUP_DEFAULT+".new","w").write("".join(content))
  395.             os.rename(self.CONSOLE_SETUP_DEFAULT+".new", 
  396.                       self.CONSOLE_SETUP_DEFAULT)
  397.         return True
  398.  
  399.     def _verify_keyboard_settings(self, model, layout, variant, options):
  400.         " helper that verfies the settings "
  401.         # check against char whitelist
  402.         allowed = "^[0-9a-zA-Z:,_]*$"
  403.         for s in (model, layout, variant, options):
  404.             if not re.match(allowed, s):
  405.                 #print "illegal chars in '%s'" % s
  406.                 return False
  407.         # check if 'ckbcomp' can compile it
  408.         cmd = ["ckbcomp"]
  409.         if model:
  410.             cmd += ["-model",model]
  411.         if layout:
  412.             cmd += ["-layout", layout]
  413.         if variant:
  414.             cmd += ["-variant", variant]
  415.         if options:
  416.             cmd += ["-option", options]
  417.         ret = subprocess.call(cmd, stdout=open(os.devnull))
  418.         return (ret == 0)
  419.  
  420.     def _run_setupcon(self):
  421.         """
  422.         helper that runs setupcon to activate the settings, taken from 
  423.         oem-config (/usr/lib/oem-config/console/console-setup-apply)
  424.         """
  425.         ret = subprocess.call(["setupcon","--save-only"])
  426.         subprocess.Popen(["/usr/sbin/update-initramfs","-u"])
  427.         return (ret == 0)
  428.  
  429.     @dbus.service.method(DBUS_INTERFACE_NAME,
  430.                          in_signature='ssss', 
  431.                          out_signature='b',
  432.                          sender_keyword='sender',
  433.                          connection_keyword='conn')
  434.     def set_keyboard(self, model, layout, variant, options, sender=None, conn=None):
  435.         """
  436.         Get the current keyboard configuration. This returns four
  437.         strings: (model, layout, variant, options)
  438.         """
  439.         #print "set_keyboard: ", model, layout, variant, options
  440.         if not self._authWithPolicyKit(sender, conn, 
  441.                                        "com.ubuntu.systemservice.setkeyboard"):
  442.             if not self._authWithPolicyKit(sender, conn,
  443.                                            "org.gnome.gconf.defaults.set-system"):
  444.  
  445.                 raise PermissionDeniedError, "Permission denied by policy"
  446.  
  447.         # if no keyboard model is set, try to guess one
  448.         # this is based on the "console-setup.config" code that
  449.         # defaults to pc105
  450.         if not model:
  451.             model = "pc105"
  452.             if layout == "us":
  453.                 model = "pc104"
  454.             elif layout == "br":
  455.                 model = "abnt2"
  456.             elif layout == "jp":
  457.                 model = "jp106"
  458.  
  459.         # verify the settings
  460.         if not self._verify_keyboard_settings(model, layout, variant, options):
  461.             #print "verify_keyboard failed"
  462.             raise InvalidKeyboardTypeError, "Invalid keyboard set"
  463.         
  464.         # apply
  465.         if not self._set_keyboard_to_etc(model, layout, variant, options):
  466.             #print "could not write keyboard to /etc"
  467.             return False
  468.         if not self._run_setupcon():
  469.             #print "setupcon failed"
  470.             return False
  471.         return True
  472.  
  473.     @dbus.service.method(DBUS_INTERFACE_NAME,
  474.                          in_signature='', 
  475.                          out_signature='b',
  476.                          sender_keyword='sender',
  477.                          connection_keyword='conn')
  478.     def is_package_system_locked(self, sender=None, conn=None):
  479.         """
  480.         Check if the package system is locked
  481.         """
  482.         #print "set_keyboard: ", model, layout, variant, options
  483.         if not self._authWithPolicyKit(sender, conn, 
  484.                                        "com.ubuntu.systemservice.ispkgsystemlocked"):
  485.                 raise PermissionDeniedError, "Permission denied by policy"
  486.         # check for file
  487.         if not os.path.exists(self.DPKG_LOCK):
  488.             return True
  489.         # check for lock
  490.         flk=struct.pack('hhllhl',fcntl.F_WRLCK,0,0,0,0,0)
  491.         f=open(self.DPKG_LOCK)
  492.         rv = fcntl.fcntl(f, fcntl.F_GETLK, flk)
  493.         lockv = struct.unpack('hhllhl', rv)[0]
  494.         f.close()
  495.         return (lockv == fcntl.F_WRLCK)
  496.  
  497. if __name__ == "__main__":
  498.     dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 
  499.     server = ServiceBackend()
  500.     gobject.MainLoop().run()
  501.  
  502.